class Mvvm{
    constructor(options){
        this.$el = options.el;
        this.$data = options.data;

        if(this.$el){
            //数据劫持
            new Observer(this.$data);

            //模板编译
            new Compile(this.$el,this);  //根节点  mvvm的实例
        }
    }
}

class Dep{
    constructor(){
        this.watchers = [];
    }
    //添加观察者
    add(watcher){
        this.watchers.push(watcher)
    }

    //通知观察者更新
    notify(){
        this.watchers.forEach(watcher => {
            watcher.update();
        })
    }

}

//观察者
class Watcher{
    constructor(vm,expr,cb){
        this.vm = vm;
        this.expr = expr;
        this.cb = cb;
        //获取之前的旧值
        this.oldVal = this.getVal();
    }

    //获取值
    getVal(){
        Dep.target = this;
        return compileUtil.getVal(this.expr,this.vm);
        Dep.target = null;
    }

    //更新
    update(){
        //修改之后的值
        let newVal =  compileUtil.getVal(this.expr,this.vm);
        if(newVal != this.oldVal){
            //旧值和新值
            this.cb(newVal);
        }
    }
}

//vm.$watch(vm,'user.username',(newVal) => {})

//定义Observer数据劫持
class Observer{
    constructor(data){
        this.data = data;
        this.observer(this.data);
    }

    observer(data){
        /**
         * {
         *      user:{
         *          username:
         *          age:'
         *      },
         *      info:
         * }
         * */ 
        if(data && typeof data === 'object'){
            for(let key in data){
                this.observerData(data,key,data[key]);
            }
        }
    }

    observerData(data,key,value){
        let dep = new Dep();
        this.observer(value);
        Object.defineProperty(data,key,{
            set(newVal){
                if(newVal != value){
                    value = newVal;
                    dep.notify();
                }
            },
            get(){
                Dep.target && dep.add(Dep.target);
                return value
            }
        })
    }
}

//模板编译
class Compile{
    constructor(el,vm){
        this.el = this.isElementNode(el) ? el : document.querySelector(el);
        this.vm = vm;
        //把根节点下的所有子节点放到文档片段内
        let fragment = this.nodeToFragment(this.el);

        //进行模板的编译
        this.compile(fragment);
        //编译的结果放回主dom树
        this.el.appendChild(fragment);
    }

    //判断当前属性是否是vue的指令
    isDirective(name){
        return name.startsWith('v-')
    }

    //元素节点
    compileElement(node){
        let attrs = node.attributes;

        [...attrs].forEach(attr => {
            let {name,value:expr} = attr; 
            //vue 指令都是以 v-开头
            if(this.isDirective(name)){
                //进行编译 
                let directive = name.split('-')[1];
                //根据指令的表达式去data里找到对应的值，进行替换，初始化视图
                compileUtil[directive](node,expr,this.vm)
            }
        })
    }

    //编译文本节点
    compileText(node){
        let content = node.textContent;
        console.log(content)
        //回车  回车 {{user.ussername}}
        if(/\{\{(.+?)\}\}/.test(content)){
            //编译文本节点
            compileUtil.content(node,content,this.vm);
        }
    }

    //编译
    compile(node){
        //根节点下的所有子节点，只获取的是一级子节点
        let childNodes = node.childNodes;

        [...childNodes].forEach(child => {
            if(this.isElementNode(child)){
                //元素节点
                this.compileElement(child);
                this.compile(child);
            }else{
                //文本节点
                this.compileText(child);
            }
        })
    }

    nodeToFragment(node){
        //1.创建文档片段
        let fragment = document.createDocumentFragment();
        //2.获取子节点
        let childNodes = node.childNodes;

        console.log(childNodes);
        [...childNodes].forEach(child => {
            fragment.appendChild(child);
        })

        console.log(fragment)
        return fragment

    }


    //判断是否是元素节点
    isElementNode(node){
        return node.nodeType === 1
    }
}

let compileUtil = {
    //根据指令的表达式去data里找到对应的值
    getVal(expr,vm){
        //[user,username]  prev:初始值和上一次计算的返回值  cur：第一次循环user  第二次循环username
        return expr.split('.').reduce((prev,cur) => {
            return prev[cur]
        },vm.$data)
    },
    //设置值
    setVal(expr,value,vm){
        //[user,username]  {username:'xx'}
        expr.split('.').reduce((prev,cur,index,arr) => {
            if(index === (arr.length-1)){
                prev[cur] = value
            }
            return prev[cur]
        },vm.$data)
    },
    model(node,expr,vm){
        // node  user.username {user:{username:'',age:}}
        let val = this.getVal(expr,vm);
        let fn = this.updater.model;
        fn(node,val);
        //从data里获取值，就添加观察者
        new Watcher(vm,expr,(newVal) => {
            fn(node,newVal);
        })
        node.addEventListener('input',(e) => {
            // e.target.value  {user:{username:xxx}}  expr
            this.setVal(expr,e.target.value,vm);
        })
    },
    content(node,content,vm){
        let fn = this.updater.content;
        //{{user.username}}
        let value = content.replace(/\{\{(.+?)\}\}/g,(...argv) => {
            console.log(argv[1])
            let val = this.getVal(argv[1],vm);
            //从data里获取值，就添加观察者
            new Watcher(vm,argv[1],(newVal) => {
                fn(node,newVal);
            })
            return val
        })
        fn(node,value);
    },
    updater:{
        model(node,value){
            node.value = value;
        },
        content(node,value){
            node.textContent = value;
        }
    }
}